﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace AttendanceDataInteraction
{
    public class MyTCPClient
    {
        public string m_Ip;
        protected int m_Port;
        protected IPEndPoint m_RemoteEP;

        TcpClient m_Client;
        bool m_AutoReconnect;
        int m_Timeout;

        byte[] m_ReadBuffer = new byte[1024];
        ManualResetEvent m_eventRecieve = new ManualResetEvent(false);

        bool m_NewDataRecieved = false;
        string m_LastResponse = "";

        System.Timers.Timer m_TimerHeartbeat;
        System.Timers.Timer m_TimerCheckConnectStatue;
        bool m_Heartbeat;
        byte[] m_RecvBuffer;

        public bool ConnectStatus = false;
        public string ClientType = "";

        public MyTCPClient(string ip, int port)
        {
            m_Ip = ip;
            m_Port = port;
            m_Client = null;
            m_AutoReconnect = false;
            m_Timeout = 500;
            m_Heartbeat = false;
        }

        public MyTCPClient(string ip, int port, bool auto_reconnect)
        {
            m_Ip = ip;
            m_Port = port;
            m_Client = null;
            m_AutoReconnect = auto_reconnect;
            if (m_AutoReconnect == true)
            {
                m_TimerCheckConnectStatue = new System.Timers.Timer(10000);
                m_TimerCheckConnectStatue.Elapsed += new System.Timers.ElapsedEventHandler(m_TimerCheckConnectStatue_Elapsed);
                m_TimerCheckConnectStatue.Start();
            }
            m_Timeout = 500;
            m_Heartbeat = false;
        }

        public MyTCPClient(string ip, int port, bool auto_reconnect, int timeout)
        {
            m_Ip = ip;
            m_Port = port;
            m_Client = null;
            m_AutoReconnect = auto_reconnect;
            if (m_AutoReconnect == true)
            {
                m_TimerCheckConnectStatue = new System.Timers.Timer(10000);
                m_TimerCheckConnectStatue.Elapsed += new System.Timers.ElapsedEventHandler(m_TimerCheckConnectStatue_Elapsed);
                m_TimerCheckConnectStatue.Start();
            }
            m_Timeout = timeout;
            m_Heartbeat = false;
        }

        public MyTCPClient(string ip, int port, bool auto_reconnect, int timeout, bool heartbeat)
        {
            m_Ip = ip;
            m_Port = port;
            m_Client = null;
            m_AutoReconnect = auto_reconnect;
            m_Timeout = timeout;
            m_Heartbeat = heartbeat;
        }

        void m_TimerCheckConnectStatue_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
        {
            if (ConnectStatus == false)
            {
                Trace.WriteLine(System.DateTime.Now.ToString() + ": " + "Try to ReConnect Server :" + m_Ip + " " + ClientType);
                if (!Connect())
                {
                    Trace.WriteLine(System.DateTime.Now.ToString() + ": " + "Reconnct Server: " + m_Ip + " Failed !" + " " + ClientType);
                }
                else
                {
                    Trace.WriteLine(System.DateTime.Now.ToString() + ": " + "Reconnct Server: " + m_Ip + " Success !" + " " + ClientType);
                }
            }
        }

        public bool Connect()
        {
            if (m_Client != null) return true;

            if (m_RemoteEP == null)
            {
                try
                {
                    m_RemoteEP = new IPEndPoint(IPAddress.Parse(m_Ip), m_Port);
                }
                catch (Exception ex)
                {
                    m_RemoteEP = null;
                    m_Client = null;
                }
            }
            if (m_RemoteEP == null)
                return false;
            try
            {
                m_Client = new TcpClient();
                m_Client.SendTimeout = m_Timeout;
                m_Client.ReceiveTimeout = m_Timeout;
                m_Client.Connect(m_RemoteEP);
                ConnectStatus = true;
                NetworkStream stream = m_Client.GetStream();
                stream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length, new AsyncCallback(ReadCallBack), stream);
                if (m_Heartbeat)
                {
                    m_TimerHeartbeat = new System.Timers.Timer();
                    m_TimerHeartbeat.Elapsed += new System.Timers.ElapsedEventHandler(SendHeartbeat);
                    m_TimerHeartbeat.Interval = m_Timeout;
                }
                return true;
            }
            catch (Exception ex)
            {
                m_Client = null;
                ConnectStatus = false;
            }
            return false;
        }

        void SendHeartbeat(object sender, System.Timers.ElapsedEventArgs e)
        {
            SendHeartbeatData();
        }

        public virtual void SendHeartbeatData()
        {
        }

        private void ReadCallBack(IAsyncResult ar)
        {
            if (!ConnectStatus) return;
            NetworkStream stream = (NetworkStream)ar.AsyncState;
            try
            {
                int bytesRecieved = stream.EndRead(ar);
                if (bytesRecieved == 0) return;
                m_NewDataRecieved = true;

                byte[] data = new byte[bytesRecieved];
                Buffer.BlockCopy(m_ReadBuffer, 0, data, 0, bytesRecieved);
                m_LastResponse = System.Text.Encoding.Default.GetString(data);
                m_RecvBuffer = data;
                ProcessDataRecv(data);
                if (DataRecv != null)
                {
                    DataRecv(data);
                }
                try
                {
                    stream.BeginRead(m_ReadBuffer, 0, m_ReadBuffer.Length, new AsyncCallback(ReadCallBack), stream);
                }
                catch (Exception ex1)
                {
                    Trace.WriteLine(System.DateTime.Now.ToString() + ": " + this.m_Ip + "\t" + ex1);
                    ConnectStatus = false;
                    m_Client = null;
                    return;
                }
            }
            catch (Exception ex2)
            {
                Trace.WriteLine(System.DateTime.Now.ToString() + ": " + this.m_Ip + "\t" + ex2);
                ConnectStatus = false;
                m_Client = null;
                return;
            }
        }

        public virtual void ProcessDataRecv(byte[] data)
        {
            ;
        }

        public bool SendMsg(string msg)
        {
            return SendData(System.Text.Encoding.Default.GetBytes(msg));
        }

        public bool SendData(byte[] data)
        {
            if (m_AutoReconnect)
            {
                if (!CheckClient())
                    return false;
            }
            try
            {
                NetworkStream stream = m_Client.GetStream();
                stream.BeginWrite(data, 0, data.Length, new AsyncCallback(WriteCallBack), stream);
            }
            catch (Exception ex)
            {
                Trace.WriteLine(System.DateTime.Now.ToString() + ": " + ex);
                m_Client = null;
                return false;
            }
            return true;
        }

        public void WriteCallBack(IAsyncResult ar)
        {
            NetworkStream stream = (NetworkStream)ar.AsyncState;
            try
            {
                stream.EndWrite(ar);
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex);
                m_Client = null;
            }
        }

        private bool CheckClient()
        {
            if (m_Client == null || (!m_Client.Connected) || (!m_Client.GetStream().CanRead) || (!m_Client.GetStream().CanWrite))
            {
                m_Client = null;
                return Connect();
            }
            return true;
        }

        /// <summary>
        /// 发送单个请求字符串并接收回复数据
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        public string Request(string data)
        {
            m_eventRecieve.Reset();
            m_NewDataRecieved = false;
            SendMsg(data);
            m_eventRecieve.WaitOne(m_Timeout, false);
            if (!m_NewDataRecieved) return "";
            return m_LastResponse;
        }

        public byte[] Request(byte[] data)
        {
            m_eventRecieve.Reset();
            m_NewDataRecieved = false;
            SendData(data);
            m_eventRecieve.WaitOne(m_Timeout, false);
            if (!m_NewDataRecieved) return new byte[] { };
            return m_RecvBuffer;
        }

        public bool Disconnect()
        {
            ConnectStatus = false;
            if (m_TimerHeartbeat != null)
            {
                m_TimerHeartbeat.Enabled = false;
                m_TimerHeartbeat.Dispose();
            }
            if (m_Client != null)
            {
                try
                {
                    m_Client.GetStream().Close();
                    m_Client.GetStream().Dispose();
                    m_Client.Close();
                    Trace.WriteLine(System.DateTime.Now.ToString() + ": Disconnect from Server. IP: " + m_Ip);
                }
                catch (Exception ex)
                {
                    Trace.WriteLine(System.DateTime.Now.ToString() + ": " + ex);
                }
            }
            return true;
        }

        public delegate void DataRecvDelegate(byte[] data);
        public event DataRecvDelegate DataRecv;
    }
}
